Skip to content

fix(desktop): allow valid git characters in branch name sanitization - #1470

Merged
Kitenite merged 2 commits into
mainfrom
kitenite/branch-name-linting-too-strict
Feb 13, 2026
Merged

Kitenite merged 2 commits into
mainfrom
kitenite/branch-name-linting-too-strict

Conversation

@Kitenite

@Kitenite Kitenite commented Feb 13, 2026 •

Copy link
Copy Markdown
Member

Closes #1454
Closes #1455

Summary

  • Branch name sanitization (sanitizeSegment) was too strict — only allowed [a-z0-9\s-], stripping valid git characters like underscores, dots, plus signs, and at signs
  • Expanded the allowlist to match git-check-ref-format rules while still guarding against forbidden patterns

Changes

  • branch.ts: Changed allowlist from /[^a-z0-9\s-]/ to /[^a-z0-9._+@-]/ so underscores (_), dots (.), plus (+), and at signs (@) are preserved
  • branch.ts: Added guards for git-forbidden patterns: consecutive dots (..), @{ sequences, .lock suffix, and leading/trailing dots
  • branch.test.ts: Added tests for all newly allowed characters, git-forbidden pattern handling, and forbidden character stripping (~^:?*[\)

Test Plan

  • bun test apps/desktop/src/shared/utils/branch.test.ts — 29 tests pass
  • Create a workspace with branch names containing _, ., +, @ and verify they are preserved
  • Verify branch names with git-forbidden chars (~^:?*[\) are still stripped

Summary by CodeRabbit

  • Bug Fixes

    • Improved branch name sanitization: preserves underscores, dots, plus signs, and at signs; collapses consecutive dots; removes .lock suffix and leading/trailing dots/hyphens; stricter removal of invalid characters.
    • Branch names generated from titles now use the updated sanitization logic for consistent results.
  • Tests

    • Expanded tests to cover new character allowances, edge cases (empty strings, dot sequences, special sequences) and updated expectations.

The sanitizeSegment allowlist was too restrictive, only permitting
[a-z0-9\s-]. Expanded to also allow underscores, dots, plus signs,
and at signs — all valid per git-check-ref-format. Added guards for
git-forbidden patterns (.., @{, .lock suffix).
@coderabbitai

coderabbitai Bot commented Feb 13, 2026 •

Copy link
Copy Markdown
Contributor

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Walkthrough

Sanitization for branch-related strings was tightened and expanded: allowed characters now include underscores, dots, pluses, and at-signs; consecutive dots are collapsed; @{ sequences normalized; .lock suffix and leading/trailing dots/dashes removed. Tests and generateBranchFromTitle now use the updated sanitizeSegment behavior.

Changes

Cohort / File(s) Summary
Branch sanitization utility & tests
apps/desktop/src/shared/utils/branch.ts, apps/desktop/src/shared/utils/branch.test.ts
Expanded allowed characters in sanitizeSegment (adds _, ., +, @), collapse consecutive dots, normalize @{ sequences, strip trailing .lock and leading/trailing dots/dashes; updated tests for sanitizeSegment, sanitizeAuthorPrefix, and sanitizeBranchName to cover these cases and empty-string behavior.
Renderer: branch generation usage
apps/desktop/src/renderer/routes/_authenticated/_dashboard/project/$projectId/page.tsx
generateBranchFromTitle now delegates slugification to sanitizeSegment(title) (import added) instead of its previous inline slug pipeline; retains authorPrefix handling and empty-slug fallback.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Poem

🐰 I hopped through regex fields at night,
Let dots and underscores stay in sight,
Snipped .lock tails, smoothed double-dots away,
I hummed a tidy branch-name ballet,
Now branches bound with cheer — hip-hop hooray! 🎩✨

🚥 Pre-merge checks | ✅ 2 | ❌ 2
❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Merge Conflict Detection ⚠️ Warning ❌ Merge conflicts detected (3 files):

⚔️ apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/components/PresetsBar/PresetsBar.tsx (content)
⚔️ apps/desktop/src/shared/utils/branch.test.ts (content)
⚔️ apps/desktop/src/shared/utils/branch.ts (content)

These conflicts must be resolved before merging into main.
Resolve conflicts locally and push changes to this branch.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically summarizes the main change: allowing valid git characters in branch name sanitization, which is the core objective of the PR.
Description check ✅ Passed The description covers most required sections with sufficient detail: Summary and Changes are comprehensive; Testing section is present with test results and manual verification plans.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch kitenite/branch-name-linting-too-strict

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@apps/desktop/src/shared/utils/branch.ts`:
- Line 10: The leading-character strip regex in the replace call
(.replace(/^[-.]|[-.]+$/g, "")) only removes a single leading dot or dash;
update the pattern used in that replace to remove runs of leading mixed
dots/dashes symmetrically (e.g., change the left alternation to use a + like the
right side) so inputs like ".-hello" have all leading punctuation removed;
locate the replace invocation in branch.ts and replace the regex /^[-.]|[-.]+$/g
with a regex that uses + on the leading side (making it /^[-.]+|[-.]+$/g).
- Around line 6-8: The final .replace(/@\{/g, "@") call in the string
normalization chain is dead code because the earlier .replace(/[^a-z0-9._+@-]/g,
"") already strips the "{" character; remove the unreachable .replace(/@\{/g,
"@") from the replace chain in apps/desktop/src/shared/utils/branch.ts (the
normalization pipeline that applies .replace(/[^a-z0-9._+@-]/g, "") and
.replace(/\.{2,}/g, ".")). This keeps behavior identical and removes the
redundant operation; alternatively, if you prefer normalization of the original
input first, move the @\{→@ replacement to run before the allowlist replace, but
prefer simply deleting the dead .replace(/@\{/g, "@") call.
- Line 11: The current .replace(/\.lock$/g, "") only strips one trailing ".lock"
so names like "foo.lock.lock" remain invalid; update the .replace call in
branch.ts to use a pattern that matches one or more repeated ".lock" suffixes at
the end (i.e., a greedy/quantified pattern that removes all consecutive ".lock"
occurrences) so the sanitization fully strips any repeated ".lock" endings.
🧹 Nitpick comments (1)
apps/desktop/src/shared/utils/branch.test.ts (1)

21-47: Good coverage of the newly allowed characters and git-forbidden patterns.

Consider adding edge-case tests for the issues noted in branch.ts:

// mixed leading chars
test("removes multiple leading hyphens and dots", () => {
  expect(sanitizeSegment(".-hello")).toBe("hello");
});

// repeated .lock
test("removes repeated .lock suffix", () => {
  expect(sanitizeSegment("foo.lock.lock")).toBe("foo");
});

These would currently fail, confirming the bugs flagged in the implementation review.

Comment on lines +6 to +8
.replace(/[^a-z0-9._+@-]/g, "")
.replace(/\.{2,}/g, ".")
.replace(/@\{/g, "@")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

@{ replacement on line 8 is dead code — { is already stripped by line 6.

The allowlist on line 6 ([^a-z0-9._+@-]) does not include {, so { is removed before the @{ replacement on line 8 ever runs. For example, "test@{0}" becomes "test@0" at line 6 already.

The test still passes because line 6 handles it, but line 8 is unreachable. Either remove line 8 or reorder lines 7-8 before line 6 so the @{ → @ normalization runs on the original input first.

Suggested fix: remove the dead line
 		.replace(/[^a-z0-9._+@-]/g, "")
 		.replace(/\.{2,}/g, ".")
-		.replace(/@\{/g, "@")
 		.replace(/-+/g, "-")
🤖 Prompt for AI Agents
In `@apps/desktop/src/shared/utils/branch.ts` around lines 6 - 8, The final
.replace(/@\{/g, "@") call in the string normalization chain is dead code
because the earlier .replace(/[^a-z0-9._+@-]/g, "") already strips the "{"
character; remove the unreachable .replace(/@\{/g, "@") from the replace chain
in apps/desktop/src/shared/utils/branch.ts (the normalization pipeline that
applies .replace(/[^a-z0-9._+@-]/g, "") and .replace(/\.{2,}/g, ".")). This
keeps behavior identical and removes the redundant operation; alternatively, if
you prefer normalization of the original input first, move the @\{→@ replacement
to run before the allowlist replace, but prefer simply deleting the dead
.replace(/@\{/g, "@") call.

.replace(/@\{/g, "@")
.replace(/-+/g, "-")
.replace(/^-|-$/g, "")
.replace(/^[-.]|[-.]+$/g, "")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Leading-character strip only removes a single [-.], not a run of mixed chars.

/^[-.]|[-.]+$/g — the ^[-.] (no +) matches exactly one character. If the input after earlier steps has a sequence like .-hello, only the . is removed, leaving -hello. The trailing side correctly uses [-.]+$. The leading side should be symmetrical:

Suggested fix
-		.replace(/^[-.]|[-.]+$/g, "")
+		.replace(/^[-.]+|[-.]+$/g, "")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.replace(/^[-.]|[-.]+$/g, "")
.replace(/^[-.]+|[-.]+$/g, "")
🤖 Prompt for AI Agents
In `@apps/desktop/src/shared/utils/branch.ts` at line 10, The leading-character
strip regex in the replace call (.replace(/^[-.]|[-.]+$/g, "")) only removes a
single leading dot or dash; update the pattern used in that replace to remove
runs of leading mixed dots/dashes symmetrically (e.g., change the left
alternation to use a + like the right side) so inputs like ".-hello" have all
leading punctuation removed; locate the replace invocation in branch.ts and
replace the regex /^[-.]|[-.]+$/g with a regex that uses + on the leading side
(making it /^[-.]+|[-.]+$/g).

.replace(/-+/g, "-")
.replace(/^-|-$/g, "")
.replace(/^[-.]|[-.]+$/g, "")
.replace(/\.lock$/g, "")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Repeated .lock suffix is not fully stripped.

A single .replace(/\.lock$/g, "") pass turns foo.lock.lock into foo.lock, which still violates git check-ref-format. Consider a greedy pattern:

Suggested fix
-		.replace(/\.lock$/g, "")
+		.replace(/(\.lock)+$/g, "")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.replace(/\.lock$/g, "")
.replace(/(\.lock)+$/g, "")
🤖 Prompt for AI Agents
In `@apps/desktop/src/shared/utils/branch.ts` at line 11, The current
.replace(/\.lock$/g, "") only strips one trailing ".lock" so names like
"foo.lock.lock" remain invalid; update the .replace call in branch.ts to use a
pattern that matches one or more repeated ".lock" suffixes at the end (i.e., a
greedy/quantified pattern that removes all consecutive ".lock" occurrences) so
the sanitization fully strips any repeated ".lock" endings.

The project page had an inline generateBranchFromTitle with a duplicate
of the old strict regex. Replace it with the shared sanitizeSegment
utility so both flows use the same branch name logic.
@Kitenite
Kitenite merged commit 3b6718c into main Feb 13, 2026
4 of 6 checks passed
@Kitenite
Kitenite deleted the kitenite/branch-name-linting-too-strict branch February 13, 2026 21:31
@github-actions

Copy link
Copy Markdown
Contributor

🧹 Preview Cleanup Complete

The following preview resources have been cleaned up:

  • ⚠️ Neon database branch
  • ⚠️ Electric Fly.io app
  • ⚠️ Streams Fly.io app

Thank you for your contribution! 🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[feat] Don't remove word separators in auto-generated branch names. [bug] regression: advanced options -> branch name is ignored

1 participant